import os
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from threading import Thread
from typing import Optional, Dict, List, Set
import matplotlib
matplotlib.use('TkAgg')
from UAS.memetic_workflow import MemeticWorkflow
import time
import os
import glob
import json
from datetime import datetime

# Import components
from UAS.gui.components.file_settings_panel import FileSettingsPanel
from UAS.gui.components.parameter_settings_panel import ParameterSettingsPanel
from UAS.gui.components.algorithm_control_panel import AlgorithmControlPanel
from UAS.gui.components.results_display import ResultsDisplay
from UAS.gui.components.visualization_panel import VisualizationPanel

class MainWindow(tk.Tk):
    """主窗口类，负责管理GUI界面和算法控制"""
    
    # 算法预设参数
    PRESETS = {
        "快速": {'population_size': 50, 'max_iterations': 50, 
               'max_mutation_prob': 0.2, 'min_mutation_prob': 0.01},
        "中速": {'population_size': 100, 'max_iterations': 200,
               'max_mutation_prob': 0.4, 'min_mutation_prob': 0.01},
        "慢速": {'population_size': 500, 'max_iterations': 500,
               'max_mutation_prob': 0.2, 'min_mutation_prob': 0.01}
    }

    def get_resource_path(self, relative_path):
        """获取资源文件的绝对路径，始终使用运行路径"""
        base_path = os.path.abspath(".")
        return os.path.join(base_path, relative_path)

    def __init__(self):
        """初始化主窗口"""
        super().__init__()
        self.title("无人机最优路径计算器")
        self.geometry("1200x800")
        
        # 确保results目录存在
        results_dir = self.get_resource_path("results")
        os.makedirs(results_dir, exist_ok=True)
        
        # 设置窗口图标
        try:
            from PIL import Image, ImageTk
            icon_path = self.get_resource_path("logo.png")
            img = Image.open(icon_path)
            icon = ImageTk.PhotoImage(img)
            self.iconphoto(False, icon)
        except Exception as e:
            print(f"Failed to load icon: {str(e)}")
        
        # 算法相关属性
        self.workflow: Optional[MemeticWorkflow] = None
        self.worker_thread: Optional[Thread] = None
        
        # 数据监控相关属性
        self.iteration_data: List[Dict] = []
        self.profit_data: List[float] = []
        self.cost_data: List[float] = []
        self.processed_iterations: Set[int] = set()
        self.monitoring_active: bool = False
        
        self.create_widgets()
        self.style_config()
        # 设置初始状态
        self.set_initial_state()
        
    def set_initial_state(self):
        """设置初始界面状态"""
        self.file_settings.enable_widgets()
        self.validate_btn.config(state=tk.NORMAL)
        self.disable_parameter_inputs()
        self.algorithm_control.enable_run_button()
        self.algorithm_control.disable_speed_preset()
        
    def set_validation_passed_state(self):
        """校验通过后的界面状态"""
        self.enable_parameter_inputs()
        self.algorithm_control.enable_speed_preset()
        
    def set_running_state(self):
        """算法运行时的界面状态"""
        self.file_settings.disable_widgets()
        self.validate_btn.config(state=tk.DISABLED)
        self.disable_parameter_inputs()
        self.algorithm_control.disable_run_button()
        self.algorithm_control.disable_speed_preset()
        
    def set_stopped_state(self):
        """算法停止后的界面状态"""
        self.file_settings.enable_widgets()
        self.validate_btn.config(state=tk.NORMAL)
        self.enable_parameter_inputs()
        self.algorithm_control.enable_run_button()
        self.algorithm_control.enable_speed_preset()
        
    def set_error_state(self):
        """错误发生时的界面状态"""
        self.set_initial_state()

    def create_widgets(self):
        """创建并布局所有GUI组件"""
        # Main container
        main_frame = ttk.Frame(self)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Left panel
        left_panel = ttk.Frame(main_frame, width=300)
        left_panel.pack(side=tk.LEFT, fill=tk.Y)

        # 文件设置面板
        self.file_settings = FileSettingsPanel(left_panel, 
                                             self.load_points, 
                                             self.load_matrix)
        self.file_settings.pack(fill=tk.X, pady=5)
        
        # 校验按钮
        self.validate_btn = ttk.Button(left_panel, 
                                     text="校验文件",
                                     command=self.validate_files)
        self.validate_btn.pack(fill=tk.X, pady=5)

        # 参数设置面板
        self.param_settings = ParameterSettingsPanel(left_panel)
        self.param_settings.pack(fill=tk.X, pady=5)

        # 算法控制面板
        self.algorithm_control = AlgorithmControlPanel(left_panel,
                                                     self.update_preset,
                                                     self.start_algorithm,
                                                     self.stop_algorithm)
        self.algorithm_control.pack(fill=tk.X, pady=5)

        # Right panel
        right_panel = ttk.Frame(main_frame)
        right_panel.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        # Create and pack results display
        self.results_display = ResultsDisplay(right_panel)
        self.results_display.pack(fill=tk.BOTH, expand=True)

        # Create and pack visualization panel
        self.visualization = VisualizationPanel(right_panel)
        self.visualization.pack(fill=tk.BOTH, expand=True)

        # Add version label in bottom left corner
        #self.version_label = ttk.Label(left_panel, text="version-1.0.0",
        #                             foreground="#808080", font=("微软雅黑", 8))
        #self.version_label.pack(side=tk.BOTTOM, anchor=tk.W, pady=5)

    def style_config(self):
        """配置GUI样式"""
        style = ttk.Style()
        style.theme_use('clam')
        style.configure('.', font=('微软雅黑', 10))
        style.configure('TButton', padding=5)
        style.configure('TLabel', padding=5)
        style.configure('TFrame', background='#DCDAD5')
        style.map('TButton', 
                foreground=[('active', 'white'), ('!active', 'white')],
                background=[('active', '#FF453A'), ('!active', '#FF6B35')],
                relief=[('active', 'raised'), ('!active', 'flat')])

    def load_points(self):
        """加载点数据文件"""
        filepath = filedialog.askopenfilename(
            title="选择点位与积分信息EXCEL文件",
            filetypes=[("Excel文件", "*.xlsx")]
        )
        if filepath:
            self.file_settings.lbl_points.config(text=filepath)

    def load_matrix(self):
        """加载矩阵数据文件"""
        filepath = filedialog.askopenfilename(
            title="选择预估的点与点之间用时信息EXCEL文件",
            filetypes=[("Excel文件", "*.xlsx")]
        )
        if filepath:
            self.file_settings.lbl_matrix.config(text=filepath)

    def update_preset(self):
        """更新算法预设参数"""
        selected = self.algorithm_control.speed_var.get()
        if selected in self.PRESETS:
            self.workflow = MemeticWorkflow()
            self.workflow.config.update(self.PRESETS[selected])

    def start_algorithm(self):
        """启动算法"""
        if not self.worker_thread or not self.worker_thread.is_alive():
            try:
                self.clear_display()
                # 清空最终结果窗口
                for widget in self.results_display.result_frame.winfo_children():
                    widget.destroy()
                self.cleanup_previous_results()
                self.set_running_state()
                self.reset_monitoring_state()
                
                if not self.monitoring_active:
                    self.start_checking_results()
                
                self.initialize_workflow()
                self.worker_thread = Thread(target=self.run_algorithm)
                self.worker_thread.start()
            except ValueError as e:
                messagebox.showerror("输入错误", "请输入有效的数值")
                self.set_error_state()
            except Exception as e:
                messagebox.showerror("启动错误", str(e))
                self.set_error_state()

    def clear_display(self):
        """清除显示内容"""
        self.results_display.process_output.config(state=tk.NORMAL)
        self.results_display.process_output.delete(1.0, tk.END)
        self.results_display.process_output.config(state=tk.DISABLED)
        self.visualization.ax1.clear()
        self.visualization.canvas.draw()

    def cleanup_previous_results(self):
        """清理之前的结果文件"""
        try:
            results_dir = self.get_resource_path("results")
            for f in glob.glob(os.path.join(results_dir, 'results_*.json')):
                try:
                    os.remove(f)
                except Exception:
                    pass
        except Exception as e:
            print(f"清理结果文件时出错: {str(e)}")

    def reset_monitoring_state(self):
        """重置监控状态"""
        self.processed_iterations.clear()
        self.iteration_data.clear()
        self.profit_data.clear()
        self.cost_data.clear()

    def validate_files(self):
        """执行文件校验并检查路径违规"""
        points_path = self.file_settings.lbl_points.cget("text")
        matrix_path = self.file_settings.lbl_matrix.cget("text")
        
        if not points_path or not matrix_path:
            self.show_validation_result("错误：请先选择两个文件")
            self.set_error_state()
            return
            
        try:
            from UAS.data.excel_parser import ExcelParser, StructureError
            from UAS.data.graph_builder import GraphBuilder
            from UAS.data.graph_validator import GraphValidator
            
            # 从output目录读取校验后的文件
            #parser = ExcelParser("output/valid_points.xlsx", "output/valid_matrix.xlsx")
            parser = ExcelParser(points_path, matrix_path)
            parser.load_data()
            
            # 检查路径违规
            builder = GraphBuilder(parser)
            builder.build_graph()
            validator = GraphValidator(builder)
            violations = validator.validate_paths()
                 
            # 准备输出内容
            output = []
            if violations:
                output.append(f"发现 {len(violations)} 条路径违规：\n")
                for u, v, details in violations:
                    output.append(f"[{u}] -> [{v}]\n")
                    output.append(f"{details}\n")
                output.append("\n详细日志已保存至 output/violation.log")
            else:
                output.append("未发现路径违规")
            
            # 显示结果
            self.show_validation_result("".join(output))
            
            # 保存日志
            output_dir = self.get_resource_path("output")
            os.makedirs(output_dir, exist_ok=True)
            log_path = os.path.join(output_dir, "violation.log")
            try:
                with open(log_path, "w", encoding="utf-8") as f:
                    f.write("违规路径检查结果：\n")
                    if violations:
                        for u, v, details in violations:
                            f.write(f"[{u}] -> [{v}]\n")
                            f.write(f"{details}\n\n")
                    else:
                        f.write("未发现路径违规\n")
            except Exception as e:
                print(f"写入日志文件时出错: {str(e)}")
            
            self.set_validation_passed_state()
        except StructureError as e:
            self.show_validation_result(f"校验失败：\n{str(e)}")
            self.set_error_state()
        except Exception as e:
            self.show_validation_result(f"系统错误：\n{str(e)}")
            self.set_error_state()

    def enable_parameter_inputs(self):
        """启用参数输入框"""
        self.param_settings.set_inputs_state('normal')

    def disable_parameter_inputs(self):
        """禁用参数输入框"""
        self.param_settings.set_inputs_state('disabled')

    def show_validation_result(self, message):
        """显示校验结果"""
        self.results_display.process_output.config(state='normal')
        self.results_display.process_output.delete(1.0, tk.END)
        self.results_display.process_output.insert(tk.END, message)
        self.results_display.process_output.config(state='disabled')

    def initialize_workflow(self):
        """初始化工作流"""
        if not self.workflow:
            self.workflow = MemeticWorkflow()
        # 初始化时禁用参数输入
        self.disable_parameter_inputs()
        self.workflow.callback = self.handle_workflow_update
        
        selected = self.algorithm_control.speed_var.get()
        algorithm_params = self.PRESETS[selected] if selected in self.PRESETS else {}
        
        # 获取输出文件路径
        output_dir = self.get_resource_path('output')
        output_points = os.path.join(output_dir, 'valid_points.xlsx')
        output_matrix = os.path.join(output_dir, 'valid_matrix.xlsx')
  
        # 检查输出文件是否存在
        if not os.path.exists(output_points) or not os.path.exists(output_matrix):
            raise FileNotFoundError(f"输出文件不存在，请先进行文件校验。查找路径: {output_dir}")
        
        # 传递文件路径到算法配置
        self.workflow.config.update({
            'points_path': output_points,
            'matrix_path': output_matrix,
            'time_limit': int(self.param_settings.time_limit.get()) * 10,
            'valid_time': int(self.param_settings.valid_time.get()) * 10,
            'penalty_interval': float(self.param_settings.penalty_interval.get()) * 10,
            'penalty_rate': int(self.param_settings.penalty_points.get()),
            'data_path': self.get_resource_path(''),
            **algorithm_params
        })

    def handle_workflow_update(self, data: Dict):
        """处理算法进度更新"""
        if data.get('status') == 'start':
            self.append_output("计算开始")
            return
        
        if data.get('status') == 'complete':
            self.process_final_results()
            time.sleep(5)  # 等待最终结果文件生成
            self.append_output("计算结束，请点击“最终结果”查看")
            return
            
        message = data.get('message', '')
        self.append_output(message)
        
        if 'best_profit' in data and 'iteration' in data:
            filename = f"results_{data['iteration']}.json"
            self.after(0, self.update_visualization_data, filename)

    def run_algorithm(self):
        """运行算法"""
        try:
            results = self.workflow.run_workflow()
            if self.workflow.callback:
                self.workflow.callback({"status": "complete", "message": "Algorithm finished"})
            self.after(0, self.on_finished, results)
        except Exception as e:
            self.after(0, messagebox.showerror, "运行错误", str(e))

    def stop_algorithm(self):
        """停止算法"""
        if self.worker_thread and self.worker_thread.is_alive():
            try:
                if self.workflow is not None:
                    self.workflow.terminate()
                self.monitoring_active = False
                self.worker_thread.join(timeout=1)  # 等待线程结束，最多1秒
                self.worker_thread = None
                self.workflow = None
                self.append_output("算法已终止")
                self.set_stopped_state()
            except Exception as e:
                self.append_output(f"停止算法时出错: {str(e)}")
                self.set_error_state()

    def process_final_results(self):
        """处理最终结果"""
        self.results_display.process_final_results()
        
    def on_finished(self, results):
        """算法完成时调用"""
        self.process_final_results()
        self.update_visualization()
        self.set_stopped_state()

    def append_output(self, text: str):
        """追加输出信息"""
        self.results_display.append_output(text)

    def start_checking_results(self):
        """启动定时检查results目录"""
        self.monitoring_active = True
        self.check_for_new_results()
        if self.monitoring_active:
            self.after(2000, self.start_checking_results)

    def check_for_new_results(self):
        """检查results目录中是否有新的结果文件"""
        try:
            results_dir = self.get_resource_path("results")
            result_files = glob.glob(os.path.join(results_dir, 'results_*.json'))
            
            for filepath in result_files:
                try:
                    filename = os.path.basename(filepath)
                    if filename == 'results_final.json':
                        if not os.path.exists(filepath):
                            return
                        with open(filepath) as f:
                            data = json.load(f)
                        self.update_results_display(data)
                        self.update_visualization_data('results_final.json')
                        self.monitoring_active = False
                    else:
                        iteration = int(filename.split('_')[1].split('.')[0])
                        if iteration not in self.processed_iterations:
                            with open(filepath) as f:
                                data = json.load(f)
                            self.processed_iterations.add(iteration)
                            self.update_results_display(data)
                            self.update_visualization_data(filename)
                except Exception as e:
                    print(f"处理文件 {filepath} 时出错: {str(e)}")
        except Exception as e:
            print(f"检查结果时出错: {str(e)}")

    def read_results_file(self, iteration: int) -> Optional[List]:
        """读取指定迭代次数的结果文件"""
        try:
            file_path = self.get_resource_path(os.path.join("results", f"results_{iteration}.json"))
            with open(file_path) as f:
                data = json.load(f)
                return data['results']
        except FileNotFoundError:
            return None

    def update_results_display(self, data: Dict):
        """更新结果显示"""
        iteration = data.get('iteration', 'final')
        results = data['results']
        timestamp = data['timestamp']
        
        dt = datetime.fromisoformat(timestamp)
        time_str = dt.strftime('%M:%S')
        
        self.results_display.update_results_display(data)

    def update_visualization_data(self, filename: str):
        """更新可视化数据"""
        self.visualization.update_visualization_data(filename)
        
    def update_visualization(self):
        """更新可视化"""
        self.visualization.update_visualization()

if __name__ == "__main__":
    window = MainWindow()
    window.mainloop()
